use std::io::{Error ,  ErrorKind , Read, Write};
use std::os::fd::IntoRawFd;
use std::time::Duration;
use libc::time;
use tracing::error;
// 跨平台终端句柄
pub struct Handle {
    #[cfg(unix)] 
    inner : std::os::fd::RawFd ,  
    #[cfg(windows)]
    inner : std::os::windows::io::RawHandle , 
    
    close : bool ,
    #[cfg(windows)]
    out_utf8 : bool, 
    #[cfg(windows)]
    incomplete_utf8 : super::IncompleteUtf8 ,
}

impl Drop for Handle{
    fn drop(&mut self){
        #[cfg(unix)]
        if self.close{
            unsafe{ libc::close(self.inner) } ; 
        }
        #[cfg(windows)]
        if self.close{ 
            unsafe{ windows_sys::Win32::Foundation::CloseHandle(self.inner) } ;
        }
    }
}

impl Read for Handle {
    /// 把self.inner当成file读取buf
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        #[cfg(unix)]
        {
            
            use std::os::{fd::IntoRawFd , unix::io::FromRawFd} ;
            // 将一个裸露的fd包装成一个File
            let mut f = unsafe { std::fs::File::from_raw_fd(self.inner) };  // 直接操作fd是不安全的  所以用unsafe
            // 调用标准库完成读取
            let result = f.read(buf) ;
            // fd所有权离开File
            _ = f.into_raw_fd() ;
            // read结果向上传递
            result
        }
        #[cfg(windows)]
        {
            use std::os::windows::io::{FromRawHandle , IntoRawHandle} ;
            let mut f = unsafe { std::fs::File::from_raw_handle(self.inner) };
            let result = f.read(buf) ;
            _ = f.into_raw_handle() ;
            result
        }
    }
}

impl Write for Handle {
    /// 把self.inner当成file写buf
    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
        #[cfg(unix)]
        {
            use std::os::{fd::IntoRawFd , unix::io::FromRawFd };
            // 和read类似  从fd转成file
            let mut f = unsafe { std::fs::File::from_raw_fd(self.inner) };
            // 写字节流
            let result = f.write(buf) ;
            // 取走所有权
            _ = f.into_raw_fd() ;
            // 返回写的结果Result<usize>
            result 
        }
    }

    fn flush(&mut self) -> std::io::Result<()> {
        Ok(())
    }
}


/// new poll read_u8 是终端句柄的核心方法
/// 无论进程的stdin/stdout如何被重定向  始终都能拿到可靠的控制终端并在其上执行非阻塞读
#[cfg(unix)]
impl Handle{

    // 构造函数
    pub(super) fn new(out:bool) -> Self{
        use std::{fs::OpenOptions , os::fd::IntoRawFd} ;
        use libc::{STDIN_FILENO , STDOUT_FILENO} ;
        let resort = Self {
            inner: if out { STDOUT_FILENO } else { STDIN_FILENO },
            close: false,
        } ;
        if unsafe {libc::isatty(resort.inner)} == 1 {
            return resort ;
        }
        match OpenOptions::new().read(!out).write(out).open("/dev/tty") {
            Ok(f) => Self{inner: f.into_raw_fd() , close: true} ,
            Err(err) => {
                error!("failed to open /dev/tty, falling back to stdin/stdout; {err}") ;
                resort
            }
        }
    }


    /// poll 是select的简单封装
    /// TODO : 改成epoll可能开销更大  改成poll兴许更合适一点 而且修改代价也更小
    pub(super) fn poll(&mut self, timeout: Duration) -> std::io::Result<bool> {
        let mut tv = libc::timeval {
            tv_sec : timeout.as_secs() as libc::time_t ,
            tv_usec : timeout.subsec_micros() as libc::suseconds_t ,
        };
        // 调用select
        let result = unsafe{
            let mut set : libc::fd_set = std::mem::zeroed() ;
            libc::FD_ZERO(&mut set) ;
            libc::FD_SET(self.inner , &mut set) ;
            libc::select(
                self.inner + 1 , // nfds : 待检查的最大fd+1
                         &mut set ,  // readfds
                         std::ptr::null_mut() , // writefds = Null
                         std::ptr::null_mut() ,  // exceptfds = Null
                         &mut tv)  // timeout
        } ;
        match result {
            -1 => Err(Error::last_os_error()) ,
            0 => Ok(false) ,
            _ => Ok(true) ,
        }
    }


    /// 从self.inner文件描述符一次读取一个字节
    pub(super) fn read_u8(&mut self)-> std::io::Result<u8> {
        let mut b = 0 ;
        // 关于第二参数 &mut b (可写借用) -> as *mut _ => 第一次强转成裸指针 &mut u8 -> as *mut _ => 变成 *mut c_void 满足read的签名
        match unsafe{ libc::read(self.inner, &mut b as *mut _ as *mut _  , 1)} {
            -1 => Err(Error::last_os_error()) ,
            0 => Err(Error::new(ErrorKind::UnexpectedEof , "unexpected EOF") ) ,
            _ => Ok(b) ,
        }
    }
}
// 这里详细讲讲为什么两个强制类型转化
// libc的签名 ssize_t read(int fd, void *buf, size_t count) ;  因此需要将&mut u8 转成 c_void
// 首先b的类型是&mut u8, 需要将这带生命周期，带可变借用语义的引用，变成裸指针
// 第一次 as *mut _   转化成原始裸指针  这里推断成 *mut u8   地址本身不变
// 第二次 as *mut _   告诉编译器把它指向另一种类型的裸指针，而且因为read期待的是  *mut c_void, 所以编译器就把_推断成 c_void


// TODO : windows下面的handle 没有编写